using System;
using System.Collections.Generic;
using System.Text;

namespace AntsMap
{
    class SolverAnnealing : Solver
    {
       
        private int[,] tempsol;
        private double delta;
        private double temp;
        private int tmpcuriter;


        public SolverAnnealing(int[,] soln, int dim, int elem, CostMap map )
        {
            // Make new arrays and give them the values of soln
            solution = new int[elem, dim];
            Array.Copy(soln, solution, elem * dim);
            prevsol = new int[elem, dim];
            Array.Copy(soln, prevsol, elem * dim);
            bestsol = new int[elem, dim];
            Array.Copy(soln, bestsol, elem * dim);
            tempsol = new int[elem, dim];
            Array.Copy(soln, tempsol, elem * dim);
            dimensions = dim;
            elements = elem;
            Optimal = false;
            costmap = map;
            bestcost = 1000000000; //TODO fix that silly
            //maxiter = (int)System.Math.Ceiling( (double) 10 * dim * ((Factorial(elem) / (2 * Factorial(elem - 2)))));
            maxiter = 465; // 10 * 0.99^x = 0.1 is the end condition.  use logs to find x
            curiter = 0;
            tmpcuriter = 0;
            delta = 0;
            temp = 10;
        }

        private int Factorial(int i)
        {
            return ((i <= 1) ? 1 : (i * Factorial(i - 1))); 
        }

        /*
         * permute 2 elements at a time for each dimension until all
         * possible have been explored.
         * 
         * if improve soln, store it as current and define new neighborhood
         * and search it.
         * 
         * if nothing better found, terminate and return local minimum.
         * 
         * 2-exch( S, C)
         * {
         *      Sbest = S;
         *      for( m = 0; m < Dim; m++)
         *      {
         *          for( e1 = 1; e < elem-1; e1++)
         *          {
         *              for( e2 = e1+1; e2 < elem; e2++ )
         *              {
         *                  old = cost( )
         *                  sol( e1 ) = sol( e2 );
         *                  new = cost( )
         *                  if( new < old )
         *                      Sbest = newsol
         *                      return Sbest
         *                  sol( e1 ) = sol( e2 );
         *              }
         *          }
         *      }
         *      return Sbest;
         */
        public override int[,] SolveStep()
        {
            Random r = new Random();

            while (tmpcuriter < maxiter)
            {
                // Pick random column and 2 different elements from that column
                int randcol = r.Next(0, costmap.Dimension);
                int randel1 = r.Next(0, costmap.Elements);
                int randel2 = r.Next(0, costmap.Elements);
                while (randel2 == randel1)
                    randel2 = r.Next(0, costmap.Elements);
                // Swap the values of these two elements
                int tval = solution[randel1, randcol];
                solution[randel1, randcol] = solution[randel2, randcol];
                solution[randel2, randcol] = tval;
                // solution now has the new permutation

                delta = costmap.Cost(solution) - bestcost;
                if (delta <= 0)
                {
                    bestcost = costmap.Cost(solution);
                    Array.Copy( solution, bestsol, dimensions*elements);
                    Array.Copy(solution, tempsol, dimensions * elements);
                    //tempsol = solution;
                }
                else
                {
                    double u = r.NextDouble();
                    double exp = Math.Exp(-delta / temp);
                    if (u <= exp)
                    {
                        bestcost = costmap.Cost(solution);
                        Array.Copy(solution, bestsol, dimensions * elements);
                        Array.Copy(solution, tempsol, dimensions * elements);
                    }
                    else
                        Array.Copy(tempsol, solution, dimensions * elements);
                        //solution = tempsol;  // move it back to what we initialized with
                    
                  
                }
                tmpcuriter++;
                
            }

            if (temp > 0.1)
            {
                temp *= 0.99;
                tmpcuriter = 0;
            }
            else
                Optimal = true;

            curiter++;

            return bestsol;

        }

        public override int[,] BestSolution()
        {
            return bestsol;
        }

        public override int BestCost()
        {
            return bestcost;
        }

        public override string Message()
        {
            return "Temperature: " + temp.ToString( );
        }



    }
}
